#include "../Public/glutils.h"
#include <cstdio>
#include <string>
using std::string;
#include <iostream>


namespace GLUtils {

	void APIENTRY debugCallback(GLenum source, GLenum type, GLuint id,
		GLenum severity, GLsizei length, const GLchar* msg, const void* param) {

		string sourceStr;
		switch (source) {
		case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
			sourceStr = "WindowSys";
			break;
		case GL_DEBUG_SOURCE_APPLICATION:
			sourceStr = "App";
			break;
		case GL_DEBUG_SOURCE_API:
			sourceStr = "OpenGL";
			break;
		case GL_DEBUG_SOURCE_SHADER_COMPILER:
			sourceStr = "ShaderCompiler";
			break;
		case GL_DEBUG_SOURCE_THIRD_PARTY:
			sourceStr = "3rdParty";
			break;
		case GL_DEBUG_SOURCE_OTHER:
			sourceStr = "Other";
			break;
		default:
			sourceStr = "Unknown";
		}

		string typeStr;
		switch (type) {
		case GL_DEBUG_TYPE_ERROR:
			typeStr = "Error";
			break;
		case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
			typeStr = "Deprecated";
			break;
		case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
			typeStr = "Undefined";
			break;
		case GL_DEBUG_TYPE_PORTABILITY:
			typeStr = "Portability";
			break;
		case GL_DEBUG_TYPE_PERFORMANCE:
			typeStr = "Performance";
			break;
		case GL_DEBUG_TYPE_MARKER:
			typeStr = "Marker";
			break;
		case GL_DEBUG_TYPE_PUSH_GROUP:
			typeStr = "PushGrp";
			break;
		case GL_DEBUG_TYPE_POP_GROUP:
			typeStr = "PopGrp";
			break;
		case GL_DEBUG_TYPE_OTHER:
			typeStr = "Other";
			break;
		default:
			typeStr = "Unknown";
		}

		string sevStr;
		switch (severity) {
		case GL_DEBUG_SEVERITY_HIGH:
			sevStr = "HIGH";
			break;
		case GL_DEBUG_SEVERITY_MEDIUM:
			sevStr = "MED";
			break;
		case GL_DEBUG_SEVERITY_LOW:
			sevStr = "LOW";
			break;
		case GL_DEBUG_SEVERITY_NOTIFICATION:
			sevStr = "NOTIFY";
			break;
		default:
			sevStr = "UNK";
		}

		std::cerr << sourceStr << ":" << typeStr << "[" << sevStr << "]" << "(" << id << "): " << msg << std::endl;
	}


	int checkForOpenGLError(const char* file, int line) {
		//
		// Returns 1 if an OpenGL error occurred, 0 otherwise.
		//
		GLenum glErr;
		int    retCode = 0;

		glErr = glGetError();
		while (glErr != GL_NO_ERROR)
		{
			const char* message = "";
			switch (glErr)
			{
			case GL_INVALID_ENUM:
				message = "Invalid enum";
				break;
			case GL_INVALID_VALUE:
				message = "Invalid value";
				break;
			case GL_INVALID_OPERATION:
				message = "Invalid operation";
				break;
			case GL_INVALID_FRAMEBUFFER_OPERATION:
				message = "Invalid framebuffer operation";
				break;
			case GL_OUT_OF_MEMORY:
				message = "Out of memory";
				break;
			default:
				message = "Unknown error";
			}

			printf("glError in file %s @ line %d: %s\n", file, line, message);
			retCode = 1;
			glErr = glGetError();
		}
		return retCode;
	}

	void dumpGLInfo(bool dumpExtensions) {
		const GLubyte* renderer = glGetString(GL_RENDERER);
		const GLubyte* vendor = glGetString(GL_VENDOR);
		const GLubyte* version = glGetString(GL_VERSION);
		const GLubyte* glslVersion = glGetString(GL_SHADING_LANGUAGE_VERSION);

		GLint major, minor, samples, sampleBuffers;
		glGetIntegerv(GL_MAJOR_VERSION, &major);
		glGetIntegerv(GL_MINOR_VERSION, &minor);
		glGetIntegerv(GL_SAMPLES, &samples);
		glGetIntegerv(GL_SAMPLE_BUFFERS, &sampleBuffers);

		printf("-------------------------------------------------------------\n");
		printf("GL Vendor    : %s\n", vendor);
		printf("GL Renderer  : %s\n", renderer);
		printf("GL Version   : %s\n", version);
		printf("GL Version   : %d.%d\n", major, minor);
		printf("GLSL Version : %s\n", glslVersion);
		printf("MSAA samples : %d\n", samples);
		printf("MSAA buffers : %d\n", sampleBuffers);
		printf("-------------------------------------------------------------\n");

		if (dumpExtensions) {
			GLint nExtensions;
			glGetIntegerv(GL_NUM_EXTENSIONS, &nExtensions);
			for (int i = 0; i < nExtensions; i++) {
				printf("%s\n", glGetStringi(GL_EXTENSIONS, i));
			}
		}
	}

} // namespace GLUtils